#ifdef linux
#include<GL/glew.h>
#include<GLFW/glfw3.h>
#else
#include<glew.h>
#include<glfw3.h>
#endif
#include<iostream>
#include<time.h>
#include<math.h>

//#define MVP_TEST 
#include "myBase.h"
#include <stdlib.h>
#define GLM_FORCE_RADIANS
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

class myStateCalls
{
    public:
    static void enableDepth() { glEnable(GL_DEPTH_TEST); }
};
class initState
{
    public:
};
float posZ = -10.0f;
float posX = 0.0f;

void keyCallback(GLFWwindow *wnd, int key, int scancode, int action, int mods)
{
    switch(key)
    {
        case GLFW_KEY_LEFT:
            posX += 0.05f;
        break;
        
        case GLFW_KEY_RIGHT:
            posX -= 0.05f;
        break;
        
        case GLFW_KEY_UP:
            posZ += 0.05f;
        break;
        
        case GLFW_KEY_DOWN:
            posZ -= 0.05f;
        break;
    }    
}

int main()
{
	int errorval = 0;	
	errorval = glfwInit();
    if(!errorval) return 1;
    
    myWindow *window = new myWindow(800,600, "Hello World");    
    
    window->createWindow();
    window->makeCurrent();
    
    glfwSetKeyCallback(window->getWindow(), keyCallback);

    glewExperimental = GL_TRUE;
    glewInit();
	
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    float points[] = { -30.0f, 0.0f, -30.0f,1.0f,
                       30.0f, 0.0f, -30.0f,1.0f,
                       0.0f, 30.0f, -30.0f, 1.0f
                     };
    
    //float points[] = { 0.0f, 0.5f, 0.0f,1.0f,
    //                   0.5f, -0.5f, 0.0f,1.0f,
    //                   -0.5f, -0.5f, 0.0f,1.0f
    //                 };

    float times[] = { 
                        sin(time(NULL)) * 0.5f + 0.5f, cos(time(NULL))*0.5f + 0.5f, 0,0,
                        sin(time(NULL)) * 0.5f + 0.5f, cos(time(NULL))*0.5f + 0.5f, 0,0,
                        sin(time(NULL)) * 0.5f + 0.5f, cos(time(NULL))*0.5f + 0.5f, 0,0,

                    };
    glm::mat4 View = glm::lookAt(glm::vec3(posX,0.0,posZ), glm::vec3(posX,0.0,-180.0),glm::vec3(0,1,0));
    //glm::mat4 View = glm::mat4(1.0);
    glm::mat4 Proj = glm::perspective(45.0f, 1.0f, 0.1f, 200.0f);
    glm::mat4 MVP = Proj * View;
    GLint uniformlocation = -1;

#ifdef MVP_TEST
    glm::vec4 p1 = glm::vec4(points[0], points[1], points[2], points[3]);
    glm::vec4 p2 = glm::vec4(points[4], points[5], points[6], points[7]);
    glm::vec4 p3 = glm::vec4(points[8], points[9], points[10], points[11]);
   
    p1 = MVP * p1; 
    p2 = MVP * p2; 
    p3 = MVP * p3; 


    std::cout << "\nVERTEX 1 " << std::endl;
    std::cout << "X: " << p1.x << " Y: " << p1.y << " Z: " << p1.z << " W: " << p1.w;
    
    std::cout << "\nVERTEX 1 :: AFTER DIVIDE " << std::endl;
    std::cout << "X: " << p1.x/p1.w << " Y: " << p1.y/p1.w << " Z: " << p1.z/p1.w << " W: " << 1.0f;
    
    std::cout << "\nVERTEX 2 " << std::endl;
    std::cout << "X: " << p2.x << " Y: " << p2.y << " Z: " << p2.z << " W: " << p2.w;
    
    std::cout << "\nVERTEX 2 :: AFTER DIVIDE " << std::endl;
    std::cout << "X: " << p2.x/p2.w << " Y: " << p2.y/p2.w << " Z: " << p2.z/p2.w << " W: " << 1.0f;
    
    std::cout << "\nVERTEX 3 " << std::endl;
    std::cout << "X: " << p3.x << " Y: " << p3.y << " Z: " << p3.z << " W: " << p3.w;
    
    std::cout << "\nVERTEX 3 :: AFTER DIVIDE " << std::endl;
    std::cout << "X: " << p3.x/p3.w << " Y: " << p3.y/p3.w << " Z: " << p3.z/p3.w << " W: " << 1.0f << std::endl;

    return 0 ;
#endif
    
    GLuint vbo = 0;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), points, GL_STATIC_DRAW);

    GLuint vbo1 = 0;
    glGenBuffers(1, &vbo1);
    glBindBuffer(GL_ARRAY_BUFFER, vbo1);
    glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), times, GL_DYNAMIC_DRAW);

    GLuint vao = 0;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(0,4, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);


    glBindBuffer(GL_ARRAY_BUFFER, vbo1);
    glVertexAttribPointer(1,4, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(1);


    myShader *vshader = new myShader(myShader::vertexShader, "Vertex Color Pass And Rotate ");
    myShader *pshader = new myShader(myShader::fragmentShader, "Fragment Color Mix");
    myShaderPipe *pipe = new myShaderPipe();

    vshader->createShaderFromFile("mvp_vertex.glsl");
    pshader->createShaderFromFile("mvp_fragment.glsl");
    vshader->compileShader();
    pshader->compileShader();
    
    pipe->createProgram();
    pipe->attachShader(vshader);
    pipe->attachShader(pshader);
    pipe->linkPipe();

    uniformlocation = glGetUniformLocation(pipe->getHandle(), "rotMatrix");
    if(uniformlocation == -1) {
        std::cout << "\nERROR : Invalid uniform location " ;
        exit(1);
    }
    float sign = 1;
    while(!glfwWindowShouldClose(window->getWindow()))
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUseProgram(pipe->getHandle());
        glBindVertexArray(vao);
        times[0] = times[4] = times[8] = sin(time(NULL)) * 0.5f + 0.5f;
        times[1] = times[5] = times[9] = cos(time(NULL)) * 0.5f + 0.5f;
        glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), times, GL_DYNAMIC_DRAW);
        glUniformMatrix4fv(uniformlocation, 1, GL_FALSE, glm::value_ptr(MVP));
        View = glm::lookAt(glm::vec3(posX,0.0,posZ), glm::vec3(posX,0.0,-180.0f),glm::vec3(0,1,0));
        MVP = Proj * View; 
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glfwPollEvents();
        glfwSwapBuffers(window->getWindow());
    }
    glfwTerminate();
	return 0;
}
